/*
    OQUERY.C

Written by DavidW
Started on 28/11/91
OO aspects started 1/9/93

*/

#include <p_std.h>
#include <p_math.h>
#include <p_date.h>
#include <p_object.h>
#include <p_crypt.h>
#include <p_file.h>
#include <wlib.h>
#include <hwif.h>

#include <oqd.g>

#include "oquery.rsg"


LOCAL_D INT DylHandle;

LOCAL_D TEXT *cmds[]=
    {
    "iInches/Centimetres",
    "mMiles/Kilometres",
    "lPounds/Kilogrammes",
    "pPints/Litres",
    "fFahrenheit/Centigrade",
    "dDay of week",
    "cCombinations",
    "tTime difference",
    "hHoroscope",
    "nNow",
    "wPassword",
    "eEncrypt",
    "uDecrypt",
    "sSignificance",
    "zFile size",
    "xExit",
    NULL
    };

#define CONVERT_ITEMS       5
#define CALENDAR_ITEMS      5
#define SECRET_ITEMS        3
#define SPECIAL_ITEMS       3

#define ITEMS_BEFORE_CONVERT    0
#define ITEMS_BEFORE_CALENDAR   CONVERT_ITEMS
#define ITEMS_BEFORE_SECRET     (ITEMS_BEFORE_CALENDAR+CALENDAR_ITEMS)
#define ITEMS_BEFORE_SPECIAL    (ITEMS_BEFORE_SECRET+SECRET_ITEMS)

LOCAL_D H_MENU_DATA mdata[]=
    {
    "Conversions",CONVERT_ITEMS,
    "Calendar",CALENDAR_ITEMS,
    "Secret",SECRET_ITEMS,
    "Special",SPECIAL_ITEMS,
    NULL
    };

GLDEF_D TEXT ** _cmds=(&cmds[0]);
GLDEF_D H_MENU_DATA * _mdata=(&mdata[0]);


LOCAL_D UINT MainWid;

LOCAL_D WORD ndp=2;
LOCAL_D DOUBLE small;
LOCAL_D DOUBLE vsmall;

LOCAL_D TEXT pass[10];

LOCAL_D VOID *ttH;
LOCAL_D UWORD ttMonth=1;
LOCAL_D UWORD ttSuffix=1;
LOCAL_D UWORD ttDay=1;
LOCAL_D UWORD ttCentury=2;
LOCAL_D UWORD ttSeconds=2;


LOCAL_C VOID ZeroDouble(DOUBLE *pdbl)
    {
    p_bfil(pdbl,sizeof(DOUBLE),0);
    }

LOCAL_C VOID MakeDouble(DOUBLE *pdbl,WORD val)
    {
    p_itof(pdbl,&val);
    }

LOCAL_C VOID CalcSmall(VOID)
    {
    DOUBLE dndp;
    DOUBLE temp;

    MakeDouble(&dndp,ndp);
    p_fneg(&dndp);
    MakeDouble(&temp,10);
    p_pow(&small,&temp,&dndp);
    MakeDouble(&temp,2);
    p_fld(&vsmall,&small);
    p_fdiv(&vsmall,&temp);
    }

LOCAL_C VOID Clip(DOUBLE *pval)
    {
    DOUBLE remain;
    DOUBLE temp;

    MakeDouble(&temp,0);
    if (p_fcmp(pval,&temp)>=0)
        p_fadd(pval,&vsmall);
    else
        p_fsub(pval,&vsmall);
    p_mod(&remain,pval,&small);
    p_fsub(pval,&remain);
    }

LOCAL_C VOID FahrToCent(DOUBLE *pcent,DOUBLE *pfahr)
    {
    DOUBLE temp;

    p_fld(pcent,pfahr);
    MakeDouble(&temp,32);
    p_fsub(pcent,&temp);
    MakeDouble(&temp,5);
    p_fmul(pcent,&temp);
    MakeDouble(&temp,9);
    p_fdiv(pcent,&temp);
    }

LOCAL_C VOID CentToFahr(DOUBLE *pfahr,DOUBLE *pcent)
    {
    DOUBLE temp;

    p_fld(pfahr,pcent);
    MakeDouble(&temp,9);
    p_fmul(pfahr,&temp);
    MakeDouble(&temp,5);
    p_fdiv(pfahr,&temp);
    MakeDouble(&temp,32);
    p_fadd(pfahr,&temp);
    }

LOCAL_C VOID Temperatures(VOID)
    {
    DOUBLE fahr;
    DOUBLE cent;
    H_DI_FLOAT ffahr;
    H_DI_FLOAT fcent;
    INT index;

    MakeDouble(&fahr,32);
    MakeDouble(&cent,0);
    ffahr.value=(&fahr);
    MakeDouble(&ffahr.low,-17968);
    MakeDouble(&ffahr.high,18032);
    fcent.value=(&cent);
    MakeDouble(&fcent.low,-10000);
    MakeDouble(&fcent.high,10000);
    FOREVER
        {
        if (uOpenDialog("Convert temperature"))
            return;
        if (uAddDialogItem(H_DIALOG_FLOAT,"Fahrenheit",&ffahr))
            return;
        if (uAddDialogItem(H_DIALOG_FLOAT,"Centigrade",&fcent))
            return;
        index=uRunDialog();
        if (index<=0)
            break;
        if (index==2)
            {
            FahrToCent(&cent,&fahr);
            Clip(&cent);
            }
        else
            {
            CentToFahr(&fahr,&cent);
            Clip(&fahr);
            }
        }
    }

LOCAL_C VOID Convert(TEXT *pmt1,TEXT *pmt2,INT high1,INT high2)
    {
    DOUBLE val1;
    DOUBLE val2;
    H_DI_FLOAT fled1;
    H_DI_FLOAT fled2;
    INT index;
    DOUBLE ratio;

    ZeroDouble(&val1);
    ZeroDouble(&val2);
    fled1.value=(&val1);
    ZeroDouble(&fled1.low);
    MakeDouble(&fled1.high,high1);
    fled2.value=(&val2);
    ZeroDouble(&fled2.low);
    MakeDouble(&fled2.high,high2);
    p_fld(&ratio,&fled2.high);
    p_fdiv(&ratio,&fled1.high);
    FOREVER
        {
        if (uOpenDialog("Convert"))
            return;
        if (uAddDialogItem(H_DIALOG_FLOAT,pmt1,&fled1))
            return;
        if (uAddDialogItem(H_DIALOG_FLOAT,pmt2,&fled2))
            return;
        index=uRunDialog();
        if (index<=0)
            break;
        if (index==2)
            {
            p_fld(&val2,&val1);
            p_fmul(&val2,&ratio);
            Clip(&val2);
            }
        else
            {
            p_fld(&val1,&val2);
            p_fdiv(&val1,&ratio);
            Clip(&val1);
            }
        }
    }

LOCAL_C VOID InchAndCm(VOID)
    {
    Convert("Inches","Centimetres",10000,25400);
    }

LOCAL_C VOID MileAndKm(VOID)
    {
    Convert("Miles","Kilometres",20000,32187);
    }

LOCAL_C VOID PoundAndKg(VOID)
    {
    Convert("Pounds","Kilograms",31938,14500);
    }

LOCAL_C VOID PintAndL(VOID)
    {
    Convert("Pints","Litres",28156,16000);
    }

LOCAL_C VOID ChangeNdp(VOID)
    {
    LONG lndp;
    H_DI_NUMBER num;

    if (uOpenDialog("Level of significance"))
        return;
    lndp=ndp;
    num.value=(&lndp);
    num.low=0;
    num.high=4;
    if (uAddDialogItem(H_DIALOG_NUMBER,"Decimal places",&num))
        return;
    if (uRunDialog()<=0)
        return;
    ndp=(WORD)lndp;
    CalcSmall();
    }

LOCAL_C VOID SetUpDiDate(H_DI_DATE *pdate,P_DAYSEC *pds)
    {
    ULONG sdate;

    sdate=p_date();
    p_sttods(&sdate,pds);
    pdate->value=(&pds->day);
    pdate->low=0;
    pdate->high=H_LAST_DAY;
    }

LOCAL_C VOID DayOfWeek(VOID)
    {
    P_DAYSEC ds;
    H_DI_DATE date;
    TEXT buf[40];
    H_DI_TEXT txt;

    SetUpDiDate(&date,&ds);
    do
        {
        p_nmday(&buf[1],p_wkday(ds.day));
        buf[0]=p_slen(&buf[1]);
        txt.str=(&buf[0]);
        txt.type=H_DTEXT_ALIGN_LEFT;
        if (uOpenDialog("Find day of week"))
            return;
        if (uAddDialogItem(H_DIALOG_DATE,"Date",&date))
            return;
        if (uAddDialogItem(H_DIALOG_TEXT,"Day of week",&txt))
            return;
        } while (uRunDialog()>0);
    }

LOCAL_D TEXT *orient[]=
    {
    "Rat","Cow","Tiger","Rabbit","Dragon","Snake",
    "Horse","Sheep","Monkey","Rooster","Dog","Pig"
    };

LOCAL_D TEXT *western[]=
    {
    "Capricorn","Aquarius","Pisces","Aries","Taurus","Gemini",
    "Cancer","Leo","Virgo","Libra","Scorpio","Sagittarius"
    };

LOCAL_C INT Zodiac(P_DATE *pdt)
    {
    INT ret;
    LOCAL_D changeover[12]={19,18,19,19,20,20,22,22,22,22,21,20};

    ret=pdt->month;
    if (pdt->day>changeover[ret])
        {
        if (ret++==11)
            ret=0;
        }
    return(ret);
    }

LOCAL_C VOID Horoscope(VOID)
    {
    P_DAYSEC ds;
    H_DI_DATE date;
    P_DATE dt;
    INT key;
    TEXT buf[40];

    SetUpDiDate(&date,&ds);
    FOREVER
        {
        if (uOpenDialog("Horoscope"))
            return;
        if (uAddDialogItem(H_DIALOG_DATE,"Date of birth",&date))
            return;
        if (uAddButtonList("Stop",W_KEY_ESCAPE,
                                        "Western",'w',"Oriental",'o',NULL))
            return;
        key=uRunDialog();
        if (key<=0)
            return;
        p_dstodt(&ds,&dt);
        if (key=='o')
            p_atos(&buf[0],"Oriental horoscope is %s",orient[dt.year%12]);
        else
            p_atos(&buf[0],"Western horoscope is %s",western[Zodiac(&dt)]);
        wInfoMsg(&buf[0]);
        }
    }

LOCAL_C INT AddListOfDays(UWORD *pdiw)
    {
    H_DI_CHOICE ch;
    TEXT nam[32];
    INT i;

    if (uBeginDCL(&ch))
        return(-1);
    for (i=0; i<7; i++)
        {
        p_nmday(&nam[0],i);
        if (uGrowDCL(&ch,&nam[0]))
            return(-1);
        }
    return(uAddDCL("Day in week",pdiw,&ch));
    }

LOCAL_C VOID SetUpTime(H_DI_TIME *ptime,P_DAYSEC *pds)
    {
    ptime->value=(&pds->sec);
    ptime->low=0;
    ptime->high=P_NSECDAY-1;
    ptime->type=H_DTIME_SHOW_SECONDS;
    }

LOCAL_C VOID TimeDifference(VOID)
    {
    P_DAYSEC ds1;
    H_DI_DATE date1;
    H_DI_TIME time1;
    P_DAYSEC ds2;
    H_DI_DATE date2;
    H_DI_TIME time2;
    LONG diff;
    TEXT buf[40];

    SetUpDiDate(&date1,&ds1);
    SetUpDiDate(&date2,&ds2);
    SetUpTime(&time1,&ds1);
    SetUpTime(&time2,&ds2);
    FOREVER
        {
        if (uOpenDialog("Time difference"))
            return;
        if (uAddDialogItem(H_DIALOG_DATE,"From",&date1))
            return;
        if (uAddDialogItem(H_DIALOG_TIME,"",&time1))
            return;
        if (uAddDialogItem(H_DIALOG_DATE,"To",&date2))
            return;
        if (uAddDialogItem(H_DIALOG_TIME,"",&time2))
            return;
        if (uRunDialog()<=0)
            return;
        diff=ds2.day-ds1.day;
        if (diff<0 || !diff && ds2.sec<ds1.sec)
            {
            wInfoMsg("`To' date is before `From' date!");
            continue;
            }
        if (diff>0x7fffffffl/P_NSECDAY)
            {
            wInfoMsg("Dates are too far apart");
            continue;
            }
        p_atos(&buf[0],"Dates are %ld seconds apart",
                                            diff*P_NSECDAY+ds2.sec-ds1.sec);
        wInfoMsg(&buf[0]);
        }
    }

LOCAL_C INT AddNoYesChoiceList(TEXT *pmt,UWORD *pval)
    {
    return(uAddChoiceList(pmt,pval,"No","Yes",NULL));
    }

LOCAL_C VOID TimeNow(VOID)
    {
    INT values;
    H_DI_TEXT txt;
    TEXT buf[48];

    txt.str=(&buf[0]);
    do
        {
        if (uOpenDialog(NULL))
            return;
        uZTStoBCS(&buf[0],"Time is now");
        txt.type=H_DTEXT_ALIGN_CENTRE;
        if (uAddDialogItem(H_DIALOG_TEXT,NULL,&txt))
            return;
        values=0;
        if (ttMonth==2)
            values=H_TIME_FORMAT_MONTH_NAME;
        if (ttDay==2)
            values|=H_TIME_FORMAT_DAY_NAME;
        if (ttSuffix==2)
            values|=H_TIME_FORMAT_SUFFIX;
        if (ttCentury==1)
            values|=H_TIME_FORMAT_NO_CENTURY;
        if (ttSeconds==1)
            values|=H_TIME_FORMAT_NO_SECONDS;
        hTTSetFormat(ttH,values);
        hTTSetTime(ttH,H_TIME_SET_NOW,NULL);
        buf[0]=hTTSenseString(ttH,H_TIME_SENSE_BOTH,&buf[1]);
        txt.type=H_DTEXT_ALIGN_CENTRE|H_DTEXT_UNDERLINE;
        if (uAddDialogItem(H_DIALOG_TEXT,NULL,&txt))
            return;
        if (AddNoYesChoiceList("Give month name",&ttMonth))
            return;
        if (AddNoYesChoiceList("Give day name",&ttDay))
            return;
        if (AddNoYesChoiceList("Use date suffix",&ttSuffix))
            return;
        if (AddNoYesChoiceList("Show century",&ttCentury))
            return;
        if (AddNoYesChoiceList("Show seconds",&ttSeconds))
            return;
        } while (uRunDialog()>0);
    }

LOCAL_C VOID EnterPassword(VOID)
    {
    TEXT pass1[10];
    TEXT pass2[10];
    H_DI_XINPUT x1;
    H_DI_XINPUT x2;

    x1.str=(&pass1[0]);
    x2.str=(&pass2[0]);
    FOREVER
        {
        if (uOpenDialog(NULL))
            return;
        if (uAddDialogItem(H_DIALOG_XINPUT,"Define password",&x1))
            return;
        if (uAddDialogItem(H_DIALOG_XINPUT,"Confirm password",&x2))
            return;
        if (uRunDialog()<=0)
            return;
        if (p_bcmpi(&pass1[1],pass1[0],&pass2[1],pass2[0]))
            {
            wInfoMsg("Password not confirmed");
            continue;
            }
        if (pass1[0])
            break;
        wInfoMsg("Please supply password");
        }
    p_bfil(&pass[0],10,0);
    p_bcpy(&pass[0],&pass1[0],pass1[0]+1);
    }

LOCAL_C VOID MakeReadable(TEXT *out,TEXT *in)
    {
    INT len;
    INT i;
    INT val;

    i=32;
    len=(*in++);
    *out++=len*2;
    while (len--)
        {
        val=(*in++);
        *out++=i+(val&0xf);
        i+=16;
        *out++=i+(val>>4);
        if (i==112)
            i=32;
        else
            i+=16;
        }
    }

LOCAL_C VOID MakeUnreadable(TEXT *out,TEXT *in)
    {
    INT len;
    INT i;
    INT val;

    i=32;
    len=(*in++)/2;
    *out++=len;
    while (len--)
        {
        val=(*in++)-i;
        i+=16;
        val+=((*in++)-i)<<4;
        *out++=val;
        if (i==112)
            i=32;
        else
            i+=16;
        }
    }

LOCAL_C VOID Report(TEXT *title,TEXT *mess)
    {
    H_DI_TEXT txt;

    if (uOpenDialog(title))
        return;
    txt.str=mess;
    txt.type=H_DTEXT_ALIGN_CENTRE;
    if (uAddDialogItem(H_DIALOG_TEXT,NULL,&txt))
        return;
    uRunDialog();
    }

LOCAL_C VOID EncryptMessage(VOID)
    {
    P_MASK mask;
    H_DI_EDIT ed;
    TEXT in[20];
    TEXT out[38];

    if (!pass[0])
        {
        wInfoMsg("Can't encrypt with no password set");
        return;
        }
    p_setmask((VOID **)&mask,&pass[1]);
    if (uOpenDialog("Encrypt message"))
        return;
    ed.str=(&in[0]);
    ed.len=18;
    in[0]=0;
    if (uAddDialogItem(H_DIALOG_EDIT,"Message",&ed))
        return;
    if (uRunDialog()<=0)
        return;
    p_encrypt(&mask,&in[1],in[0]);
    MakeReadable(&out[0],&in[0]);
    Report("Encrypted message",&out[0]);
    }

LOCAL_C VOID DecryptMessage(VOID)
    {
    P_MASK mask;
    H_DI_SEDIT sed;
    TEXT in[38];
    TEXT out[20];

    if (!pass[0])
        {
        wInfoMsg("Can't decrypt with no password set");
        return;
        }
    p_setmask((VOID **)&mask,&pass[1]);
    if (uOpenDialog("Decrypt message"))
        return;
    sed.str=(&in[0]);
    sed.len=36;
    sed.width=16;
    in[0]=0;
    if (uAddDialogItem(H_DIALOG_SEDIT,"Message",&sed))
        return;
    if (uRunDialog()<=0)
        return;
    MakeUnreadable(&out[0],&in[0]);
    p_decrypt(&mask,&out[1],out[0]);
    Report("Decrypted message",&out[0]);
    }

LOCAL_C VOID FileSize(VOID)
    {
    TEXT fname[P_FNAMESIZE+2];
    H_DI_FSEL fsel;
    P_INFO info;
    TEXT buf[30];

    fname[0]=0;
    fsel.fname=(&fname[0]);
    fsel.flags=H_FILE_PICK_SELECTOR;
    FOREVER
        {
        if (uOpenDialog("Find file size"))
            return;
        if (uAddDialogItem(H_DIALOG_FSEL,"File:",&fsel))
            return;
        if (uRunDialog()<=0)
            return;
        fname[1+fname[0]]=0;
        if (!uErrorValue(p_finfo(&fname[1],&info)))
            {
            p_atos(&buf[0],"File size is %lu bytes",info.size);
            wInfoMsg(&buf[0]);
            }
        }
    }

LOCAL_C VOID ManageCommand(INT index)
    {
    switch (index)
        {
    case ITEMS_BEFORE_CONVERT:
        InchAndCm();
        break;
    case ITEMS_BEFORE_CONVERT+1:
        MileAndKm();
        break;
    case ITEMS_BEFORE_CONVERT+2:
        PoundAndKg();
        break;
    case ITEMS_BEFORE_CONVERT+3:
        PintAndL();
        break;
    case ITEMS_BEFORE_CONVERT+4:
        Temperatures();
        break;
    case ITEMS_BEFORE_CALENDAR:
        DayOfWeek();
        break;
    case ITEMS_BEFORE_CALENDAR+1:
        hOODialog(DylHandle,C_OQD_COMBINS,OQD_DATE_COMBINS,NULL);
        break;
    case ITEMS_BEFORE_CALENDAR+2:
        TimeDifference();
        break;
    case ITEMS_BEFORE_CALENDAR+3:
        Horoscope();
        break;
    case ITEMS_BEFORE_CALENDAR+4:
        TimeNow();
        break;
    case ITEMS_BEFORE_SECRET:
        EnterPassword();
        break;
    case ITEMS_BEFORE_SECRET+1:
        EncryptMessage();
        break;
    case ITEMS_BEFORE_SECRET+2:
        DecryptMessage();
        break;
    case ITEMS_BEFORE_SPECIAL:
        ChangeNdp();
        break;
    case ITEMS_BEFORE_SPECIAL+1:
        FileSize();
        break;
    case ITEMS_BEFORE_SPECIAL+2:
        p_exit(0);
        }
    }

LOCAL_C VOID TryExecuteCommand(INT keycode)
    {
    keycode=uLocateCommand(keycode);
    if (keycode>=0)
        ManageCommand(keycode);
    }

LOCAL_C VOID MainLoop(VOID)
    {
    INT ret;
    WMSG_KEY key;

    FOREVER
        {
        uGetKey(&key);
        if (key.keycode&W_EVENT_KEY)
            {
            if (key.keycode==CONS_EVENT_COMMAND)
                p_exit(0);
            }
        else if (key.keycode&W_SPECIAL_KEY)
            TryExecuteCommand(key.keycode&(~W_SPECIAL_KEY));
        else if (key.keycode==W_KEY_MENU && !(key.modifiers&W_CTRL_MODIFIER))
            {
            ret=uPresentMenus();
            if (ret>0)
                TryExecuteCommand(ret);
            }
        }
    }

LOCAL_C VOID ReduceScreenSize(VOID)
    {
    W_WINDATA wd;

    wd.extent.tl.x=wd.extent.tl.y=0;
    wd.extent.width=189;
    wd.extent.height=80;
    wSetWindow(MainWid,W_WIN_EXTENT,&wd);
    if (uErrorValue(wCheckPoint()))
        p_exit(0);
    }

LOCAL_C VOID CreateGC(VOID)
    {
    INT hgc;

    hgc=gCreateGC0(MainWid);
    if (hgc<0)
        p_exit(hgc);
    }

LOCAL_C VOID SpecificInit(VOID)
    {
    hInitAppRcb();
    DylHandle=hLoadOwnDyl(0);
    hCrackCommandLine();
    MainWid=uFindMainWid();
    ReduceScreenSize();
    CreateGC();
    gBorder(W_BORD_CORNER_4);
    wsEnable();
    CalcSmall();
    ttH=hTTOpen();
    }

GLDEF_C VOID main(VOID)
    {
    uCommonInit();
    SpecificInit();
    MainLoop();
    }
